<?php

/**
 * Session Class
 * @author ReGeDa
 */
class Session extends Plugin_Object {

	/**
	 * Cookie object
	 * @var Cookie
	 */
	private $cookie;

	/**
	 * Memcache object
	 * @var Memcacher
	 */
	private $cacher;

	/**
	 * Constructor
	 */
	public function __construct() {
		parent::__construct();
		// frontend
		$this->cookie = &platcode()->plugins->import('Cookie');
		// backend
		$this->cacher = &platcode()->plugins->import('Memcacher');
	}

	/**
	 * Get persistant plugin state
	 * @override
	 * @return boolean
	 */
	public function is_persistant() {
		return TRUE;
	}

	/**
	 * Create a random captcha code and save it in cacher
	 * @return string
	 */
	public function random_code() {

		platcode()->plugins->include_class('static/String');

		// load settings
		$cookie_name = config('captcha_cookie_name');
		$timeout = config('captcha_timeout');

		// captcha index
		$index = $this->cookie->get($cookie_name);

		// try to create a new captcha session
		if (empty($index)) {
			$index = String::random('alnum');
			$this->cookie->set($cookie_name, $index, $timeout);
		}

		// increase security by index prefix
		$index = $cookie_name.$index;

		// create a new security code
		$code = String::random('numeric', 3);

		// save captcha code in memory
		$this->cacher->set($index, $code, $timeout);

		return $code;
	}

	/**
	 * Verify frontend/backend captcha code
	 * @param string $code
	 * @return boolean
	 */
	public function validate_code($code) {
		// load settings
		$cookie_name = config('captcha_cookie_name');

		// captcha index
		$index = $this->cookie->get($cookie_name);

		if ( ! empty($index)) {

			// increase security by index prefix
			$index = $cookie_name.$index;

			// read and drop a key
			$stored_code = $this->cacher->drop($index);

			return ! empty($stored_code) && strcmp($code, $stored_code) == 0;
		}

		return FALSE;
	}

	/**
	 * Open a client session
	 * @param string $id
	 * @param array $data
	 * @param integer $client_ttl
	 */
	public function allocate_client($id, $data, $client_ttl) {
		// cache in memory
		$this->cacher->set('uuid-'.$id, json_encode($data), 600); // 10 min.
		// client ticket
		$this->cookie->set('id', $id, $client_ttl);
		$this->cookie->set('hash', $data['hash'], $client_ttl);
	}

	/**
	 * Close a client session
	 * @param string $id
	 */
	public function free_client($id) {
		// remove from memory
		$this->cacher->drop('uuid-'.$id);
		// kill a client ticket
		$this->cookie->delete(array('id','hash'));
	}

	/**
	 * Check a client session
	 * Update TTL on success
	 * @param string $generator - stored function name
	 * @return boolean|string
	 */
	public function validate_client($generator) {
		$front_id = $this->cookie->get('id');

		// frontend exist
		if ($front_id !== FALSE) {
			
			$back_id = 'uuid-'.$front_id;

			// backend hash
			$session = $this->cacher->$back_id;

			// if not found
			// read from database
			if ($session === FALSE) {

				// create active query				
				$AQ = &platcode()->plugins->import('ActiveQuery');

				$stmt = database()->prepare(
					// call stored function which generate a session hash
					$AQ->set(array('id' => $front_id, 'salt' => config('password_salt')))->call($generator)
				);

				// QUERY
				try {
					
					$AQ->query($stmt);

					// read session data
					// [name, hash]
					$data = $stmt->fetch(PDO::FETCH_ASSOC);

					// convert to JSON
					$session = json_encode($data);
					
				} catch (PDOException $e) {
					// error
					return FALSE;
				}
			}
			// read from cache
			else
				$data = json_decode($session, TRUE);

			// update hash TTL in memory
			$this->cacher->set($back_id, $session, 600); // 10 min.

			// compare backend with frontend
			// return session name
			if ($data['hash'] == $this->cookie->get('hash')) {

				define('SESSION_ID', $front_id);
				define('SESSION_NAME', $data['name']);

				return TRUE;
			}
		}

		// error
		return FALSE;
	}

	/**
	 * Fetch a session name
	 * @return string|NULL
	 */
	public static function name() {
		return @constant('SESSION_NAME');
	}
}

// end of file